#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <fstream>       //文件流,文件日志输出
#include <sstream>       //字符串流
#include <filesystem>    //C++17文件系统操作，判断目录是否存在
#include <time.h>        
#include <cstdio>
#include "Mutex.hpp"

namespace LogModule
{
	using namespace LockModule;

	// 获取当前系统的时间
	std::string CurrentTime()
	{
		time_t time_stamp = ::time(nullptr); //调用time函数获取系统时间戳
		struct tm curr;                      //将时间戳转化为本地时间的tm结构体
		localtime_r(&time_stamp, &curr);     

		char buffer[1024];
		snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",
				 curr.tm_year + 1900,
				 curr.tm_mon + 1,
				 curr.tm_mday,
				 curr.tm_mday,
				 curr.tm_min,
				 curr.tm_sec);

		return buffer;
	}

	// 日志构成：构建日志字符串  2.刷新落盘(screen,file)
	const std::string defaultlogpath = "./logtxt/";  // 日志存放目录
	const std::string defaultlogname = "log.txt"; // 日志文件名

	// 1.日志等级
	enum class LogLevel
	{
		DEBUG = 1,
		INFO,
		WARNING,
		ERROR,
		FATAL
	};

	std::string level_to_string(LogLevel level)
	{
		switch (level)
		{
		case LogLevel::DEBUG:
			return "DEBUG";
		case LogLevel::INFO:
			return "INFO";
		case LogLevel::WARNING:
			return "WARNING";
		case LogLevel::ERROR:
			return "ERROR";
		case LogLevel::FATAL:
			return "FATAL";
		default:
			return "None";
		}
	}

	// 2.刷新策略
	class LogStrategy
	{
	public:
		virtual ~LogStrategy() = default;
		virtual void SyncLog(const std::string &message) = 0; // 同步日志
	};

	// 3.1控制台策略
	class ConsoleLogStrategy : public LogStrategy
	{
	public:
		ConsoleLogStrategy()
		{}
		~ConsoleLogStrategy()
		{}
		void SyncLog(const std::string &message)
		{
			LockGuard lockguard(_lock);
			std::cout << message << std::endl;
		}

	private:
		Mutex _lock;
	};

	// 3.2文件(磁盘)策略
	class FileLogStategy : public LogStrategy
	{
	public:
		FileLogStategy(const std::string &logpath = defaultlogpath, const std::string &logname = defaultlogname)
			: _logpath(logpath), _logname(logname)
		{
			LockGuard lockguard(_lock);

			// 确认_logpath是否存在，不存在则创建
			if (std::filesystem::exists(_logpath))
			{
				return;
			}
			try
			{
				// 创建目录
				std::filesystem::create_directories(_logpath);
			}
			catch (std::filesystem::filesystem_error &e)
			{
				// 捕捉创建目录失败的异常并输出日志信息
				std::cerr << e.what() << '\n';
			}
		}
		~FileLogStategy()
		{
		}
		void SyncLog(const std::string &message) // 同步日志
		{
			LockGuard lockguard(_lock);

			std::string log = _logpath + _logname; //  ./log/log.txt
			std::ofstream out(log, std::ios::app); // 日志写入，追加写入
			if (!out.is_open())  //判断打开文件是否失败，失败返回不写入
				return;
			out << message << "\n";
			out.close();
		}

	private:
		std::string _logpath;
		std::string _logname;
		Mutex _lock;
	};

	// 日志类:构建日志字符串,根据策略进行刷新
	class Logger
	{
	public:
		Logger()
		{
			// 默认采用ConsoleLogStrategy策略
			_strategy = std::make_shared<ConsoleLogStrategy>();
		}
		void EnableConsoleLog()
		{
			_strategy = std::make_shared<ConsoleLogStrategy>();
		}
		void EnableFileLog()
		{
			_strategy = std::make_shared<FileLogStategy>();
		}
		~Logger()
		{
		}

		// 一条完整的信息：[time][DEBUG][pid][main.cc][line]+可变信息("hello world"<<"3.14"<<a<<b)
		class LogMessage
		{
		public:
			LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger)
				: _currtime(CurrentTime())
				, _level(level)
				, _pid(::getpid())
				, _filename(filename)
				, _line(line)
				, _logger(logger)
			{
				std::stringstream ssbuffer;
				ssbuffer << "[" << _currtime << "] "
						 << "[" << level_to_string(_level) << "] "
						 << '[' << _pid << "] "
						 << "[" << _filename << "] "
						 << "[" << _line << "] - ";

				_loginfo = ssbuffer.str();
			}

			template <typename T>
			LogMessage &operator<<(const T &info)
			{
				std::stringstream ss;
				ss << info;
				_loginfo += ss.str();
				return *this;
			}

			~LogMessage()
			{
				if (_logger._strategy)
					_logger._strategy->SyncLog(_loginfo);
			}

		private:
			std::string _currtime; // 当前日志时间
			LogLevel _level;	   // 日志等级
			pid_t _pid;			   // 进程pid
			std::string _filename; // 源文件名称
			int _line;			   // 日志所在行号
			Logger &_logger;	   // 负责根据不同的策略进行刷新
			std::string _loginfo;  // 一条完整的日志记录
		};

		// 拷贝,临时对象，后面一旦打印完毕，临时对象就会自动被析构，自动刷新到显示器/文件
		LogMessage operator()(LogLevel level, const std::string &filename, int line)
		{
			return LogMessage(level, filename, line, *this);
		}

	private:
		std::shared_ptr<LogStrategy> _strategy; // 日志刷新的策略方案
	};

	Logger logger;

#define LOG(level) logger(level, __FILE__, __LINE__) // 预处理符
#define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()
#define ENABLE_FILE_LOG() logger.EnableFileLog()

}